// 16.3 重载与模板
/**
 * 函数模板可以被另一个模板或一个普通非模板函数重载。与往常一样，名字相同的函数必须具有不同数量或类型的参数。
 */

#include <iterator>
#include <vector>
#include <list>
#include <deque>
#include <forward_list>
#include <string>
#include <array>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
using std::swap;
using std::vector, std::list, std::deque, std::forward_list, std::string, std::array, std::stack, std::queue;
#include "../Chapter07/Sales_data.h"
#include <iostream>
using std::begin, std::cbegin, std::end, std::cend, std::find, std::accumulate, std::equal, std::fill, std::fill_n, std::back_inserter;
using std::cin, std::cout, std::endl;
using std::copy, std::replace, std::replace_copy;
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <memory>
#include <new>
using namespace std;
#include <functional>
#include "../VisualStudio2012/15/Quote.h"
#include "../VisualStudio2012/12/TextQuery.h"

int main()
{
    // 16.3 重载与模板
    // 正确定义一组重载的函数模板需要对类型间的关系及模板函数允许的有限的实参类型转换有深刻的理解。

    // 编写重载模板
    // 作为一个例子，我们将构造一组函数，它们在调试中可能很有用。我们将这些调试函数命名为debug_rep，每个函数都返回一个给定对象的string表示。
    /*
我们首先编写此函数的最通用版本，将它定义为一个模板，接受一个const对象的引用：
// 打印任何我们不能处理的类型
template<typename T> string debug_rep(const T& t)
{
  ostringstream ret; // 参见8.3节
  ret << t;          // 使用t的输出运算符打印t的一个表示形式
  return ret.str();  // 返回ret绑定的string的一个副本
}
此函数可以用来生成一个对象对应的string表示，该对象可以是任意具备输出运算符的类型。
接下来，我们将定义打印指针的debug_rep版本：
template<typename T> string debug_rep(const T* t)
{
  ostringstream ret;
  ret << "pointer:" << p;        // 打印指针本身的值
  if(p)
    ret << " " << debug_rep(*p); // 打印指针所指的对象的值
  else
    ret << "null pointer";       // 或指出p为空
  return ret.str();              // 返回string绑定的一个副本
}
注意此函数不能用于打印字符指针，因为IO库为char＊值定义了一个<<版本。此<<版本假定指针表示一个空字符结尾的字符数组，并打印数组的内容而非地址值。我们将在16.3节（第617页）介绍如何处理字符指针。
我们可以这样使用这些函数：
string s("hi");
cout << debug_rep(s) << endl;
如果我们用一个指针调用debug_rep：
cout << debug_rep(&s) << endl;
两个函数都生成可行的实例：
  · debug_rep（const string＊&），由第一个版本的debug_rep实例化而来，T被绑定到string＊。
  · debug_rep（string＊），由第二个版本的debug_rep实例化而来，T被绑定到string。
第二个版本的debug_rep的实例是此调用的精确匹配。第一个版本的实例需要进行普通指针到const指针的转换。
正常函数匹配规则告诉我们应该选择第二个模板，实际上编译器确实选择了这个版本。
    */

    // 多个可行模板
    /*
作为另外一个例子，考虑下面的调用：
const string *sp = &s;
cout << debug_rep(sp) << endl;
此例中的两个模板都是可行的，而且两个都是精确匹配：
  · debug_rep（const string＊&），由第一个版本的debug_rep实例化而来，T被绑定到string＊。
  · debug_rep（const string＊），由第二个版本的debug_rep实例化而来，T被绑定到const string。
在此情况下，正常函数匹配规则无法区分这两个函数。我们可能觉得这个调用将是有歧义的。但是，根据重载函数模板的特殊规则，此调用被解析为debug_rep（T＊），即，更特例化的版本。
    */
    // 当有多个重载模板对一个调用提供同样好的匹配时，应选择最特例化的版本。

    // 非模板和模板重载
    // 作为下一个例子，我们将定义一个普通非模板版本的debug_rep来打印双引号包围的string：
    /*
// 打印双引号包围的string
string debug_rep(const string& s)
{
  return '"' + s + '"';
}
现在，当我们对一个string调用debug_rep时：
string s("hi");
cout << debug_rep(s) << endl;
有两个同样好的可行函数：
  · debug_rep<string>（const string&），第一个模板，T被绑定到string＊。
  · debug_rep（const string&），普通非模板函数。
在本例中，两个函数具有相同的参数列表，因此显然两者提供同样好的匹配。但是，编译器会选择非模板版本。
当存在多个同样好的函数模板时，编译器选择最特例化的版本，出于相同的原因，一个非模板函数比一个函数模板更好。
    */
    // 对于一个调用，如果一个非函数模板与一个函数模板提供同样好的匹配，则选择非模板版本。

    // 重载模板和类型转换
    /*
还有一种情况我们到目前为止尚未讨论：C风格字符串指针和字符串字面常量。现在有了一个接受string的debug_rep版本，我们可能期望一个传递字符串的调用会匹配这个版本。但是，考虑这个调用：
cout << debug_rep("hi world") << endl; // 调用debug_rep(T*)
本例中所有三个debug_rep版本都是可行的：
  · debug_rep（const T&），T被绑定到char[10]。
  · debug_rep（T＊），T被绑定到const char。
  · debug_rep（const string&），要求从const char＊到string的类型转换。
对给定实参来说，两个模板都提供精确匹配：第二个模板需要进行一次（许可的）数组到指针的转换，而对于函数匹配来说，这种转换被认为是精确匹配（参见6.6.1节，第219页）。
  非模板版本是可行的，但需要进行一次用户定义的类型转换，因此它没有精确匹配那么好，所以两个模板成为可能调用的函数。与之前一样，T＊版本更加特例化，编译器会选择它。
    */

    // 缺少声明可能导致程序行为异常
    // 在定义任何函数之前，记得声明所有重载的函数版本。这样就不必担心编译器由于未遇到你希望调用的函数而实例化一个并非你所需的版本。

    return 0;
}